package top.malaoshi.webmvc;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.ClassUtils;
import top.malaoshi.easy.logging.Log;
import top.malaoshi.easy.logging.LogFactory;
import top.malaoshi.sprout.ioc.DefaultSingletonBeanRegistry;
import top.malaoshi.util.Tuple2;
import top.malaoshi.webmvc.info.ClassInfo;
import top.malaoshi.webmvc.info.MethodInfo;
import top.malaoshi.webmvc.utils.ClassUtil;

import javax.annotation.PostConstruct;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * Starter扫描处理器
 * 读取当前jar包的 /META-INF/spring.factories 文件，加载启动配置类
 */
public class AutoConfigurationImportSelector {

    static final Log LOG = LogFactory.getLog(AutoConfigurationImportSelector.class);
    DefaultSingletonBeanRegistry registry = DefaultSingletonBeanRegistry.me();

    public void handle() throws Exception {
        List<String> list = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, ClassUtils.getDefaultClassLoader());

        for(String item : list){
            execute(item);
        }
    }

    private void execute(String classPath) throws Exception {

        registry.forEachClassInfos(classInfo -> {
            if(classInfo.getClazz().getName() .equals( classPath)){
                String beanName = classInfo.getName();
                Object bean = registry.getEarlySingletonObject(beanName);
                if(bean==null){
                    return;
                }
                invokePostConstructMethods(bean);
                boolean res = invokeBeanAnnotationMethods(classInfo, bean);
                if(res) {
                    registry.registerSingletonObject(beanName, bean);
                }
            }

        });
    }

    /**
     * 调用 @PostConstruct  注解修饰的方法
     * @param bean
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private void invokePostConstructMethods(Object bean) throws InvocationTargetException, IllegalAccessException {
        Method[] methods = bean.getClass().getDeclaredMethods();
        for(Method method : methods){

            if(method.isAnnotationPresent(PostConstruct.class)){
                method.setAccessible(true);
                method.invoke(bean);
            }
        }
    }

    /**
     * 调用 @Bean 注解修饰的方法，并将返回结果放入到 singletonObjects 中
     * @param classInfo
     * @param instance
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @return true：表示全部调用；false：表示有未调用的方法
     */
    private boolean invokeBeanAnnotationMethods(ClassInfo classInfo, Object instance) throws Exception {

        List<MethodInfo> beanAnnotationMethodInfos = classInfo.getBeanAnnotationMethodInfos();
        for(MethodInfo methodInfo :beanAnnotationMethodInfos){
            Method method = methodInfo.getMethod();
            String methodName = ClassUtil.getMethodName(classInfo.getClazz(),method);

            //判断是否准备好调用 @Bean 方法
            boolean ready = false;
            /*
             * 取出方法的形参类型
             * 并从 registry 取出相匹配的类型的对象，然后进行下面处理：
             * 1.如果没有匹配类型的对象，就将 bean 放入到 beanAnnMethods 中
             * 2.如果同一个类型取出多个对象，则报错
             * 3. 如果全部能够取出相匹配的所有数据，就调用 @Bean 方法
             */
            Class<?>[] parameterTypes = method.getParameterTypes();
            Object[] parameters = new Object[parameterTypes.length];
            if (parameterTypes.length == 0){
                ready = true;
            }else {
                for (int i = 0; i < parameterTypes.length; i++) {
                    Class<?> clazz = parameterTypes[i];
                    Tuple2<String[], Object[]> tuple2 = registry.getBeanByType(clazz);
                    Object[] beans = tuple2.getSecond();
                    // 如果从bean中没有获取到对象，则不处理
                    if (beans == null || beans.length == 0) {
                        ready = false;
                        break;
//            resolverBeanAnnotationByFieldType(fieldType);
//            throw new BeanCreationException(beanName,fieldType.getName());
                    } else if (beans.length > 1) {
                        throw new Exception(classInfo.getName() + " 的 " + method.getName() + " 方法的 " + clazz.getName() + " 类型的对象有多个！");
                    } else {
                        ready = true;
                        parameters[i] = beans[0];
                    }
                }
            }

            if(ready) {

                method.setAccessible(true);
                Object res = method.invoke(instance, parameters);
                String[] names = methodInfo.getBeanAnnotationInfo().getName();
                for (String name : names) {
                    registry.registerSingletonObject(name, res);
                }
            }


        }

        return true;
    }
}
